---
description: Компонент реализующий модальное окно.
---

<Overview group="modals">

# ModalPage [tag:component]

Компонент реализующий модальное окно. Позволяет показать дополнительный контент,
удерживая пользователя в текущем контексте страницы/приложения.

</Overview>

<Playground>

```jsx
const [open, setOpen] = React.useState(false);

return (
  <>
    <Button onClick={() => setOpen(true)}>Открыть</Button>
    <ModalPage
      open={open}
      onClose={() => setOpen(false)}
      header={
        <ModalPageHeader before={<PanelHeaderBack label="Назад" onClick={() => setOpen(false)} />}>
          Информация о пользователе
        </ModalPageHeader>
      }
    >
      <Group>
        <Cell>
          <InfoRow header="Дата рождения">5 ноября 1994</InfoRow>
        </Cell>
        <Cell>
          <InfoRow header="Родной город">Санкт-Петербург</InfoRow>
        </Cell>
        <Cell>
          <InfoRow header="Место работы">Команда ВКонтакте</InfoRow>
        </Cell>
      </Group>
    </ModalPage>
  </>
);
```

</Playground>

## Область применения

- Формы для редактирования.
- Детали профиля пользователя.
- Разделы настроек, которые должны удерживать пользователя в текущем контексте приложения.

## Анатомия

`ModalPage` состоит из композиции нескольких компонентов.

**Опциональные компоненты, передающиеся пользователем**:

- [ModalPageHeader](/components/modal-page-header) – передаётся через параметр `header`;
- `ModalPageFooter` – передаётся через параметр `footer`.

**Обязательные компоненты, задающиеся библиотекой**:

- `ModalOutlet` – обёртка с фиксированной позицией для перекрытия других элементов в **DOM**;
- `ModalOverlay` – интерактивный задний фон;
- `ModalPageInternal` – инкапсулирует в себе всю логику поведения;
- `ModalPageContent` – оборачивает переданный `children` в [CustomScrollView](/components/custom-scroll-view).

## Поведение

Параметр `open` задает состояние показа/скрытия `ModalPage`. По умолчанию компонент скрыт.

При состоянии `open={false}` компонент размонтируется из DOM. Чтобы отключить это поведение, необходимо задать параметр
`keepMounted`.

Смена состояния сопровождается анимацией содержимого `ModalPage`, а также `ModalOverlay`. За сменой состояния можно следить
через события:

- `onOpen`
- `onOpened`
- `onClose` – передаёт в аргумент причину закрытия (см. описание в таблице с API)
- `onClosed`

Отличие `onOpened`/`onClosed` от `onOpen`/`onClose` в том, что они вызываются после завершения анимации.

Далее поведение зависит от разрешения экрана.

### Настольный режим

При разрешении `>= 768px` компонент `ModalPage` имеет вид **диалогового окна**.

При `open={true}`:

- параметр `size` будет ограничивать максимальную ширину окна;
- навигация через `Tab` и `Shift` + `Tab` будет зациклена на содержимом `ModalPage`.

> При открытии модального окна поверх другого модального окна может возникнуть конфликт в управлении фокусом.
> По умолчанию каждое модальное окно пытается удерживать фокус внутри себя, что может привести к некорректной работе при наличии нескольких открытых модальных окон.
>
> Чтобы избежать этой проблемы, для первого (нижнего) модального окна необходимо отключить захват фокуса с помощью свойства disableFocusTrap.
> Это позволит корректно управлять фокусом во втором (верхнем) модальном окне.

По умолчанию, пользователь может закрыть **диалоговое окно** с помощью:

- нажатия на кнопку закрытия, что вызовет событие `onClose` с аргументом `click-close-button`;
- нажатия на `ModalOverlay`, что вызовет событие `onClose` с аргументом `click-overlay`;
- нажатия на `Esc`, что вызовет событие `onClose` с аргументом `escape-key`.

### Мобильный режим

При разрешении `<= 767px` компонент `ModalPage` имеет вид панели, выдвигающейся снизу вверх (далее **bottom sheet**).

> При передаче `hasCustomPanelHeaderAfter` в [ConfigProvider](/components/config-provider) модальная страница
> сверху будет смещена на высоту [PanelHeader](/components/panel-header), чтобы кнопка закрытия модальной
> страницы не перекрывалась пользовательским "плавающим" элементом (например, панелью управления
> webview).

При `open={true}` навигация через `Tab` и `Shift` + `Tab` будет зациклена на содержимом `ModalPage` (допустимо, т.к. к устройству
может быть подключена клавиатура).

По умолчанию, пользователь может закрыть **bottom sheet** с помощью:

- нажатия на `ModalOverlay`, что вызовет событие `onClose` с аргументом `click-overlay`;
- нажатия на `Esc`, что вызовет событие `onClose` с аргументом `escape-key` (допустимо, т.к. к устройству может быть подключена
  клавиатура);
- определенного взаимодействие через тач или указатель мыши, что вызовет событие `onClose` с аргументом `swipe-down`
  (см. **Взаимодействие через тач или указатель мыши**)

#### Взаимодействие через тач или указатель мыши

> `<ConfigProvider platform="vkcom" />`
>
> При `platform === 'vkcom` описанный в этом разделе функционал полностью игнорируется, т.к. по историческим причинам
> `platform="vkcom"` форсирует условие, что сейчас используется десктопный режим. Высота **bottom sheet** будет определяться за счёт
> контента.

Для **bottom sheet** можно задавать на какой процент высоты экрана изначально должна открываться панель – этот процент задаётся
через параметр `settlingHeight`. Минимальное значение `25`, а максимальное `100`. По умолчанию используется `50`.

От `settlingHeight` зависит дальнейшее поведение **bottom sheet**:

- при `settlingHeight <= 90` у **bottom sheet** появляется возможность через перетаскивание раскрывать высоту до `100` и схлопывать
  обратно до переданного `settlingHeight`;
- при `settlingHeight > 90` значение приводится к `100` и, соответственно, **bottom sheet** всегда раскрывается на `100`.

Если необходимо, чтобы высота **bottom sheet** зависела от его контента, то стоит использовать параметр `dynamicContentHeight`,
вместо `settlingHeight`.

В обеих случаях перетаскивание **bottom sheet** изменяет прозрачность `ModalOverlay` – при перетаскивание вниз фон становится
светлее. При `settlingHeight` процент прозрачности высчитывается относительно переданного значения в `settlingHeight`.

Пользователь через перетаскивание может вызвать закрытие **bottom sheet** по следующим условиям:

- `onClose('swipe-down')` вызовется сразу же, если **bottom sheet** достиг высоты `0`, независимо закончил ли пользователь
  взаимодействие с ним или нет;
- `onClose('swipe-down')` вызовется, если после окончания взаимодействия с **bottom sheet** его высота будет:
  - _при `settlingHeight`_: меньше половины переданного значения в `settlingHeight` (пример, если `settlingHeight={50}`, то условием
    закрытие будет
    `height < 50 / 2`);
  - _при `dynamicContentHeight`_: меньше половины контента;

- `onClose('swipe-down')` вызовется при сильном смахивании вниз;
  - _при `settlingHeight`_: добавляется условии, что **bottom sheet** должен находится на высоте равной переданному в
    `settlingHeight`;

При следующих условиях возможность перетаскивания замораживается, чтобы не блокировать пользователя:

- при условии `disableContentPanningGesture={false}`, есть взаимодействие с контентом `ModalPageContent` при `scrollTop !== 0`;
- есть взаимодействие с горизонтальным скроллом;
- есть взаимодействие с текстовым полем;
- есть взаимодействие с выделенным текстом;
- есть взаимодействие с элементом вызвавшим `event.stopPropagation()` на `onTouchStart` или `onMouseDown`.
- есть взаимодействие с элементом с **data**-атрибутом `data-vkui-block-sheet-behavior`
  (в частности, используется при `disableContentPanningGesture={true}`);

## Доступность (a11y)

`ModalPage` является модальным окном (`role="dialog"`), а значит у него обязательно должно быть имя — его краткое название. Благодаря этому пользователи вспомогательных технологий знают, что это за элемент и какое у него содержимое.

Задать имя можно с помощью следующих способов:

- используя свойство `title`;
- используя свойство `aria-label`;
- используя свойство `aria-labelledby`;
- используя внутри компонент [ModalPageHeader](/components/modal-page-header). Этот компонент сам свяжется с `ModalPage` через контекст c помощью `aria-labelledby`.

## FAQ

### Как поменять максимальную ширину контента?

Используйте параметр `size`.

### Как задать фиксированную высоту контента?

Используйте параметр `height`. Высоту можно задать как числом (`height={300}`) так и в процентах (`height={'80%'}`).

> В мобильной версии на высоту по умолчанию также влияет значение `settlingHeight`. Чтобы отключить его влияние, достаточно задать
> `settlingHeight={100}`.

### Как правильно применять параметр `autoFocus`?

К сожалению, из-за особенностей **React**, `autoFocus` ломает CSS анимацию.

Чтобы исправить проблему, следует отказаться от `autoFocus` и выставлять фокус вручную при событии `onOpened`.

<Playground>

```jsx
const [open, setOpen] = React.useState(false);

const inputRef = React.useRef(null);

const handleOpen = React.useCallback(() => {
  if (inputRef.current) {
    inputRef.current.focus();
  }
}, []);

return (
  <>
    <Button onClick={() => setOpen(true)}>Открыть</Button>
    <ModalPage
      id="modal-with-auto-focus"
      open={open}
      onOpened={handleOpen}
      onClose={() => setOpen(false)}
    >
      <Div>
        <Input getRootRef={inputRef} />
      </Div>
    </ModalPage>
  </>
);
```

</Playground>

## Свойства и методы [#api]

<PropsTable name="ModalPage" />
